/**
 * @author David Young
 */
package csab.core;

import java.util.List;

import org.newdawn.slick.Graphics;
import org.newdawn.slick.tiled.TiledMap;

import csab.core.structs.QuadTree;

/**
 * Defines the screen area to render and provides
 * and interface for rendering said screen area.
 * @author David Young
 */
public class View extends Collidable {
	private int mapheight;
	private int mapwidth;
	
	/**
	 * Constructor for this view object. Takes the neccesary
	 * parameters needed to define a base screen area.
	 * @param x The starting x coordinate of the view.
	 * @param y The starting y coordinate of the view.
	 * @param w The width of the view (in pixels).
	 * @param h The height of the view (in pixels).
	 * @param mw Total map width in pixels.
	 * @param mh Total map height in pixels.
	 */
	public View(int x, int y, int w, int h, int mw, int mh)
	{
		super("Viewport");
		boundingbox.x = x;
		boundingbox.y = y;
		boundingbox.width = w;
		boundingbox.height = h;
		mapwidth = mw;
		mapheight = mh;
	}
	
	/**
	 * Center this view around an entity.
	 * @param ent The entity to center this view around.
	 * @see csab.core.Entity
	 */
	public void center(Entity ent)
	{
		int halfx = (int)ent.getX() - boundingbox.width / 2;
		int halfy = (int)ent.getY() - boundingbox.height / 2;
		//Clamp to bounds
		if(halfx < 0)
		{
			boundingbox.x = 0;
		}
		else if(halfx > mapwidth - boundingbox.width)
		{
			boundingbox.x = mapwidth - boundingbox.width;
		}
		else
			boundingbox.x = halfx;
		
		if(halfy < 0)
		{
			boundingbox.y = 0;
		}
		else if(halfy > mapheight - boundingbox.height)
		{
			boundingbox.y = mapheight - boundingbox.height;
		}
		else
			boundingbox.y = halfy;
		
	}

	/* (non-Javadoc)
	 * @see csab.core.Entity#destroy()
	 */
	@Override
	public void destroy() 
	{
		//This is a required stub, does nothing.
	}
	
	/* (non-Javadoc)
	 * @see csab.core.Collidable#doCollide(csab.core.Collidable)
	 */
	@Override
	public void doCollide(Collidable obj) {
		//This is a required stub, does nothing.
	}
	
	/* (non-Javadoc)
	 * @see csab.core.Entity#draw(int, int)
	 */
	@Override
	public void draw(int x, int y, Graphics g) {
		//This is a required stub, does nothing.
	}
	
	/**
	 * Draw entities that are contained within 
	 * or collide with this view to the screen.
	 * @param g The graphics context to render to.
	 * @param rendertree The tree with which to check
	 * Entity collisions.
	 */
	public void drawEntities(Graphics g, QuadTree rendertree)
	{
		List<Collidable> torender = rendertree.queryCollisions(this);
		for(Collidable item : torender)
		{
			if(item.isDrawable()) {
				//Adjust for Graphics context
				int screenx = (int)item.getX() - boundingbox.x;
				int screeny = (int)item.getY() - boundingbox.y;
				//And draw
				item.draw(screenx, screeny, g);
			}
		}
	}
	
	/**
	 * Draw a single entity to the screen. Does not adjust
	 * for clipping area so make sure that it is contained within
	 * the screen area.
	 * @param g The graphics context to render to.
	 * @param ent The entity to render.
	 * @see org.newdawn.slick.Graphics
	 * @see csab.core.structs.QuadTree
	 */
	public void drawEntity(Graphics g, Entity ent)
	{
		if(ent.isDrawable()) {
			//Adjust for Graphics context
			int screenx = (int)ent.getX() - boundingbox.x;
			int screeny = (int)ent.getY() - boundingbox.y;
			//And draw
			ent.draw(screenx, screeny, g);
		}
	}
	
	/**
	 * Get the height of the map in pixels.
	 * @return The height of the map in pixels.
	 */
	public int getMapheight() {
		return mapheight;
	}
	
	/**
	 * Get the width of the map in pixels.
	 * @return The width of the map in pixels.
	 */
	public int getMapwidth() {
		return mapwidth;
	}
	
	/**
	 * Render a scene from a TiledMap to the screen.
	 * @param map The map from which to draw the scene.
	 * @see org.newdawn.slick.tiled.TiledMap
	 */
	public void renderTileMap(TiledMap map)
	{
		//Get tile width/height in pixels
		int tilew = map.getTileWidth();
		int tileh = map.getTileHeight();
		
		//Calculate number of tiles to render (add 1 to allow for clipping adjustments)
		int numtilesx = (boundingbox.width / tilew) + 1;
		int numtilesy = (boundingbox.height / tileh) + 2;
		
		//Adjust clipping area
		int adjx = -1*(boundingbox.x % tilew);
		int adjy = -1*(boundingbox.y % tileh);
		
		//Calculate start tile
		int sx = boundingbox.x / tilew;
		int sy = boundingbox.y / tileh;
		map.render(adjx, adjy, sx, sy, numtilesx, numtilesy);
	}
	
	/**
	 * Render a single layer from a TiledMap to the screen.
	 * @param map The map from which to draw the layer.
	 * @param layer The index of the layer to draw.
	 * @see org.newdawn.slick.tiled.TiledMap
	 */
	public void renderTileMap(TiledMap map, int layer)
	{
		//Get tile width/height in pixels
		int tilew = map.getTileWidth();
		int tileh = map.getTileHeight();
		
		//Calculate number of tiles to render (add 1 to allow for clipping adjustments)
		int numtilesx = (boundingbox.width / tilew) + 1;
		int numtilesy = (boundingbox.height / tileh) + 2;
		
		//Adjust clipping area
		int adjx = -1*(boundingbox.x % tilew);
		int adjy = -1*(boundingbox.y % tileh);
		
		//Calculate start tile
		int sx = boundingbox.x / tilew;
		int sy = boundingbox.y / tileh;
		
		map.render(adjx, adjy, sx, sy, numtilesx, numtilesy, layer, false);
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		return "x: " + boundingbox.x + " y: " + boundingbox.y +
				" width: " + boundingbox.width + " height: " +
				boundingbox.height + " mapwidth: " + mapwidth + 
				" mapheight: " + mapheight;
	}
}
